using System;
using System.Text;
using System.Net.Mail;
using System.Security.Principal;
using System.Windows.Forms;
using SharePointUtilitiesForDevelopers.Client.Forms;

namespace SharePointUtilitiesForDevelopers.Client
{
    /// <summary>
    /// Contains methods for formatting and reporting errors.
    /// </summary>
    public static class ErrorReport
    {
        #region Methods

        /// <summary>
        /// Handles the exception.
        /// </summary>
        /// <param name="owner">The parent window.</param>
        /// <param name="ex">The ex.</param>
        public static void HandleException(IWin32Window owner, Exception ex)
        {
            var errorReportForm = new ErrorReportForm(ex, Properties.Resources.ApplicationName, ex.Message);
            errorReportForm.ShowDialog(owner);
        }

        /// <summary>
        /// Submits an error report using Smtp.
        /// </summary>
        /// <param name="exception">
        /// The exception
        /// </param>
        /// <param name="message">
        /// The message
        /// </param>
        /// <param name="hostName">
        /// The Smtp host name
        /// </param>
        /// <param name="fromAddress">
        /// The from address
        /// </param>
        /// <param name="toAddressList">
        /// The list of semi-colon delimited recipients
        /// </param>
        /// <param name="userName">
        /// The user that submitted the report
        /// </param>
        /// <param name="subject">
        /// The subject of the error report
        /// </param>
        /// <param name="userComments">
        /// User comments
        /// </param>
        /// <param name="replyToAddress">
        /// The reply to address
        /// </param>
        /// <param name="bccAddressList">
        /// The list of semi-colon delimited recipients to blind carbon copy
        /// </param>
        public static void SendErrorReportBySmtp(
            Exception exception,
            string message,
            string hostName,
            string fromAddress,
            string toAddressList,
            string userName,
            string subject,
            string userComments,
            string replyToAddress,
            string bccAddressList)
        {

            // Split the recipient lists
            var toList = SplitEmailAddressList(toAddressList);
            var bccList = SplitEmailAddressList(bccAddressList);

            // Ensure there are To addresses
            if ((toList.Length == 0) && (bccList.Length == 0))
            {
                throw new ApplicationException(Properties.Resources.ErrorReportNoRecipientsException);
            }

            // Create the mail message object
            var mailMsg = new MailMessage();

            // Set from
            mailMsg.From = new MailAddress(fromAddress);

            // Set subject
            mailMsg.Subject = subject;

            // Set reply to (if specified)
            if (!string.IsNullOrEmpty(replyToAddress))
            {
                mailMsg.ReplyTo = new MailAddress(replyToAddress);
            }

            // Add To recipient e-mail addresses
            foreach (var s in toList)
            {
                var maTo = new MailAddress(s);
                mailMsg.To.Add(maTo);

            }
                        
            // Add BCC recipient e-mail addresses
            foreach (var s in bccList)
            {
                var maTo = new MailAddress(s);
                mailMsg.Bcc.Add(maTo);

            }

            // Set the body
            mailMsg.Body = FormatErrorReport(
                exception,
                message,
                userName,
                userComments);

            mailMsg.IsBodyHtml = false;

            // send the mail
            var smtp = new SmtpClient(hostName);
            smtp.Send(mailMsg);              

        }

        /// <summary>
        /// Splits the specified list of comma-separated e-mail addresses.
        /// </summary>
        /// <param name="delimitedList">The list to split</param>
        /// <returns>An array of e-mail addresses</returns>
        public static string[] SplitEmailAddressList(string delimitedList)
        {
            return delimitedList.Split(
                new[] { ';' }, 
                StringSplitOptions.RemoveEmptyEntries);

        }

        /// <summary>
        /// Creates a formatted error report using the supplied parameters.
        /// </summary>
        /// <param name="exception">
        /// The exception that caused the exception
        /// </param>
        /// <returns>A formatted error message</returns>
        public static string FormatErrorReport(
            Exception exception)
        {
            // Delegate
            return FormatErrorReport(
                exception,
                null,
                null,
                null);

        }

        /// <summary>
        /// Creates a formatted error report using the supplied parameters.
        /// </summary>
        /// <param name="exception">
        /// The exception that caused the exception
        /// </param>
        /// <param name="message">
        /// A custom error message
        /// </param>
        /// <returns>A formatted error message</returns>
        public static string FormatErrorReport(
            Exception exception,
            string message)
        {
            // Delegate
            return FormatErrorReport(
                exception,
                message,
                null,
                null);

        }

        /// <summary>
        /// Creates a formatted error report using the supplied parameters.
        /// </summary>
        /// <param name="exception">
        /// The exception that caused the exception
        /// </param>
        /// <param name="message">
        /// A custom error message
        /// </param>
        /// <param name="userName">
        /// The name of the user providing comments
        /// </param>
        /// <param name="userComments">
        /// The user comments
        /// </param>
        /// <returns>A formatted error message</returns>
        public static string FormatErrorReport(
            Exception exception,
            string message,
            string userName,
            string userComments)
        {
            const string TEXT_SEPARATOR = "----------------------------------------------------------------------";
            const string HEAD_SEPARATOR = "**********************************************************************";
            const string SUBH_SEPARATOR = "======================================================================";

            if (string.IsNullOrEmpty(userName))
                userName = "[Username not specified]";

            //if (string.IsNullOrEmpty(userComments))
            //    userComments = "[None]";

            // Create StringBuilder to maintain publishing information.
            var strInfo = new StringBuilder();

            // create the header for user comments
            strInfo.AppendFormat(
                "{0}{1}USER COMMENTS for {2}:{1}{3}{1}{1}",
                HEAD_SEPARATOR,
                Environment.NewLine,
                userName,
                userComments);

            // environment info
            strInfo.AppendFormat("{0}{1}ENVIRONMENT INFORMATION:{1}", HEAD_SEPARATOR, Environment.NewLine);

            // Write title information for the Environment info
            strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, "Date", DateTime.Now);
            strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, ".NET Version", Environment.Version);
            strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, "OS Version", Environment.OSVersion);
            strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, "CurrentDirectory", Environment.CurrentDirectory);
            strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, "MachineName", Environment.MachineName);
            strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, "UserDomainName", Environment.UserDomainName);
            strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, "UserName", Environment.UserName);

            strInfo.AppendFormat("{0}{0}", Environment.NewLine);

            // identity info
            var identity = WindowsIdentity.GetCurrent();
            if (identity != null)
            {
                strInfo.AppendFormat("{0}{1}IDENTITY INFORMATION:{1}", HEAD_SEPARATOR, Environment.NewLine);

                strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, "UserName", identity.Name);
                strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, "IsSystem", identity.IsSystem);
                strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, "IsAuthenticated", identity.IsAuthenticated);
                strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, "AuthenticationType",
                                     identity.AuthenticationType);
                strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, "Token", identity.Token);

                strInfo.AppendFormat("{0}{0}", Environment.NewLine);
            }

            // error information
            strInfo.AppendFormat("{0}{1}EXCEPTION INFORMATION:{1}{1}", HEAD_SEPARATOR, Environment.NewLine);

            if (exception == null)
            {
                strInfo.AppendFormat("No Exception object has been provided.{0}", Environment.NewLine);
            }
            else
            {
                // Loop through each exception class in the chain of exception objects.
                var currentException = exception;	// Temp variable to hold InnerException object during the loop.
                var intExceptionCount = 1;				// Count variable to track the number of exceptions in the chain.
                do
                {
                    strInfo.AppendFormat("{0}{1}", SUBH_SEPARATOR, Environment.NewLine);

                    // Write title information for the exception object.
                    strInfo.AppendFormat("{1}) Exception Information{0}", Environment.NewLine, intExceptionCount);
                    strInfo.AppendFormat("{0}Exception FieldType: {1}", Environment.NewLine, currentException.GetType().FullName);

                    // Loop through the public properties of the exception object and record their value.
                    var aryPublicProperties = currentException.GetType().GetProperties();

                    foreach (var p in aryPublicProperties)
                    {
                        // Do not log information for the InnerException or StackTrace. This information is 
                        // captured later in the process.
                        if (p.Name != "InnerException" && p.Name != "StackTrace")
                        {
                            if (p.GetValue(currentException, null) == null)
                            {
                                strInfo.AppendFormat("{0}{1}: NULL", Environment.NewLine, p.Name);
                            }
                            else
                            {
                                strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, p.Name, p.GetValue(currentException, null));
                            }
                        }
                    }

                    // Record the StackTrace with separate label.
                    if (currentException.StackTrace != null)
                    {
                        strInfo.AppendFormat("{0}{0}{1}{0}{2}) StackTrace Information:{0}", Environment.NewLine, TEXT_SEPARATOR, intExceptionCount);
                        strInfo.AppendFormat("{0}{1}", Environment.NewLine, currentException.StackTrace);
                    }

                    // Reset the temp exception object and iterate the counter.
                    currentException = currentException.InnerException;
                    intExceptionCount++;

                } while (currentException != null);

            }

            return strInfo.ToString().Trim();

        }

        #endregion Methods

    }
}